<script setup lang="ts">
// 导入 BPMN-JS 样式
import 'bpmn-js/dist/assets/diagram-js.css' // Diagram-JS 的基本样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css' // BPMN 字体样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css' // BPMN 字体代码样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
// 导入 Activiti 的 BPMN 属性面板样式
import 'bpmn-js-properties-panel-for-activiti/dist/assets/bpmn-js-properties-panel-for-activiti.css' // Activiti 属性面板样式
import { defineComponent, ref } from 'vue'
// 导入 BPMN-JS 及其模块
import BpmnModeler from 'bpmn-js/lib/Modeler' // BPMN 模型器
import propertiesPanelModule from 'bpmn-js-properties-panel-for-activiti' // Activiti 属性面板模块
import propertiesProviderModule from 'bpmn-js-properties-panel-for-activiti/lib/provider/activiti' // Activiti 属性提供者模块
import activitiModdleExtension from 'bpmn-js-properties-panel-for-activiti/config/activiti.json' // Activiti 模型扩展配置
// 汉化
import customTranslate from '@/utils/customTranslate'
import bpmn from '../init.bpmn?raw'

defineComponent({
  name: 'BpmnDemo'
})

const emits = defineEmits(['getXml'])

const visible = ref(false)
const colorArray = ref([])

// xml，默认值是空
const xml = ref('')
// 工作流在线绘制
const modeler = ref<BpmnModeler>()

// 构建颜色数组
function initColor(colors): { stroke: string, fill: string, id: string }[] {
  // 当前执行的-----绿色边框、浅绿色填充。
  // 已经执行的-----蓝色边框、浅蓝色填充。
  // 当前登录用户执行过的-----橙色边框、黄色填充
  // 当前登录用户正在执行的----紫色边框、淡紫色填充
  // 过滤出节点，非线
  const filter = colors.filter(c => c.type === 1)
  if (filter.length > 0) {
    return filter.map(c => {
      const id = c.id.trim()
      if (c.isExecuted === 0 && c.isMyTask) {
        return { stroke: 'orange', fill: 'yellow', id }
      }
      if (c.isExecuted === 2 && c.isMyTask) {
        return { stroke: 'purple', fill: 'lavender', id }
      }
      if (c.isExecuted === 0 && !c.isMyTask) {
        return { stroke: 'blue', fill: 'lightblue', id }
      }
      if (c.isExecuted === 2 && !c.isMyTask) {
        return { stroke: 'green', fill: 'lightgreen', id }
      }
      return null
    }).filter(c => c)
  }
  return null
}

// 导入文件
async function openDiagram() {
  await modeler.value.importXML(xml.value !== '' ? xml.value : bpmn)
  // 判断是否需要渲染颜色
  if (colorArray.value.length > 0) {
    // 用于设置颜色
    const modeling = modeler.value.get('modeling')
    // 用于判断节点是否存在
    const elementRegistry = modeler.value.get('elementRegistry')
    // 整合设置的颜色
    const map = initColor(colorArray.value)
    setTimeout(() => {
      map.forEach(item => {
        const element = elementRegistry.get(item.id)
        if (element) {
          modeling.setColor(element, {
            fill: item.fill,
            stroke: item.stroke
          })
        }
      })
    }, 500)
  }
}

// 初始化
function init() {
  const customTranslateModule = {
    translate: ['value', customTranslate]
  }
  modeler.value = new BpmnModeler({
    container: '#js-canvas',
    propertiesPanel: {
      parent: '#js-properties-panel'
    },
    additionalModules: [
      propertiesPanelModule,
      propertiesProviderModule,
      customTranslateModule
    ],
    moddleExtensions: {
      activiti: activitiModdleExtension
    }
  })
  openDiagram()
}

// 下载函数
function setEncoded(data, type) {
  // 从 SVG 内容创建一个 Blob
  let blob
  let name
  if (type === 'bpmn') {
    blob = new Blob([data], { type: 'application/xml' })
    name = 'diagram.bpmn'
  } else {
    blob = new Blob([data], { type: 'image/svg+xml' })
    name = 'diagram.svg'
  }

  // 创建一个下载链接
  const link = document.createElement('a')
  link.href = URL.createObjectURL(blob)
  link.download = name
  // 模拟点击链接以触发下载
  link.click()
  // 清理资源
  URL.revokeObjectURL(link.href)
}

// 下载
function loadSave() {
  modeler.value.saveXML({ format: true }, (res, xmlStr) => {
    setEncoded(xmlStr, 'bpmn')
  })
}

// 生成xml，返回父组件，并获取xml中设置的定义名
function uploadBpmn() {
  modeler.value.saveXML({ format: true }, (res, xmlStr) => {
    const definitions = modeler.value.getDefinitions()
    let name
    if (definitions && definitions.rootElements && definitions.rootElements.length > 0) {
      name = definitions.rootElements[0].name || '未命名'
    }
    emits('getXml', xmlStr, name)
  })
}

// 校验是否符合BPMN规范
async function checkFile(file) {
  const text = await file.text() // 读取文件内容
  const bpmnJS = new BpmnModeler()
  return new Promise((resolve) => {
    bpmnJS.importXML(text, (err) => {
      resolve(err === undefined)
    })
  })
}

// 文件上传前校验
async function beforeUpload(file) {
  // 校验是否是 bpmn
  const fileType = file.name.split('.').pop()
  if (fileType !== 'bpmn') {
    // 我希望返回信息，比如不是bpmn文件
    return Promise.reject(new Error('请上传bpmn文件'))
  }
  const newVar = await checkFile(file)
  if (!newVar) {
    return Promise.reject(new Error('返回文件已损坏'))
  }
  return Promise.resolve(true)
}

/**
 * 调整节点颜色
 */
function setColor(msg: string, colorArr: any[] = []) {
  xml.value = msg
  colorArray.value = colorArr
  visible.value = true
}

defineExpose({
  visible, xml, beforeUpload, setColor
})

</script>

<template>
  <br style="display: none">
  <a-modal v-model:visible="visible" fullscreen title="流程实例进度" @open="init(xml)">
    <div v-if="visible" class="box">
      <div id="js-canvas"></div>
      <div id="js-properties-panel"></div>
    </div>
    <template #footer>
      <a-button @click.prevent="visible = false">取消</a-button>
      <a-button status="warning" @click.prevent="loadSave">下载</a-button>
      <a-button type="primary" @click="uploadBpmn">提交</a-button>
    </template>
  </a-modal>
</template>

<style scoped lang="less">
.box {
  display: flex;
  height: 100vh;

  #js-canvas {
    flex-grow: 1;
    overflow: hidden;
  }

  #js-properties-panel {
    height: 100vh;
    overflow-y: auto;
  }
}

</style>